package pers.kw.server.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import pers.kw.entity.TokenDto;
import pers.kw.exceptions.BizException;
import pers.kw.protocol.TokenContext;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
public class DistributedLockAspect {

    @Resource
    private RedissonClient redissonClient;

    @Pointcut("@annotation(pers.kw.server.config.DistributedLock)")
    public void annotationPointcut() {

    }

    /**
     * 获取注解参数
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    private DistributedLock getAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        // 获得当前访问的class
        Class<?> clazz = joinPoint.getTarget().getClass();
        // 通过ProceedingJoinPoint对象获取目标方法的签名对象，通过签名对象获取目标方法的方法名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String methodName = methodSignature.getName();
        Class<?>[] parameterTypes = methodSignature.getParameterTypes();
        Method method = clazz.getMethod(methodName, parameterTypes);
        return method.getAnnotation(DistributedLock.class);
    }

    @Around("annotationPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //1.校验登录信息
        TokenDto current = TokenContext.current();
        if (current == null) {
            //throw new BizException("请先登录。。。");
        }
        //假设已登录，企业编号
        //String userNo = current.getUserNo();
        String compCode = "KH20240301001";

        //2.获取注解参数
        DistributedLock annotation = this.getAnnotation(joinPoint);
        String lockKey = annotation.lockKey();
        int scope = annotation.scope();
        String errorTips = annotation.errorTips();
        int lockType = annotation.lockType();
        long leaseTime = annotation.leaseTime();
        long waitTime = annotation.waitTime();
        log.info("获取注解信息: {} {} {}", lockKey, scope, errorTips);
        if (scope == 1) {
            //锁本企业
            lockKey = "REDIS_LOCK:" + lockKey + ":" + compCode;
        } else {
            //锁全部用户
            lockKey = "REDIS_LOCK:" + lockKey;
        }


        // 声明变量用来存储目标方法的返回值
        Object targetReturnValue = null;

        RLock rLock = redissonClient.getLock(lockKey);
        try {
            if (lockType == 2) {
                if (rLock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS)) {
                    //处理业务逻辑
                    targetReturnValue = joinPoint.proceed(joinPoint.getArgs());
                } else {
                    // 处理加锁失败的情况
                    if (StringUtils.isNotBlank(errorTips)) {
                        throw new BizException(errorTips);
                    }
                }
            } else {
                rLock.lock(leaseTime, TimeUnit.SECONDS);
                //处理业务逻辑
                targetReturnValue = joinPoint.proceed(joinPoint.getArgs());
            }
            return targetReturnValue;
        } catch (InterruptedException e) {
            // 处理加锁时发生的中断异常
            log.error("加锁异常：", e);
            throw e;
        } catch (Throwable e) {
            log.error("执行异常：", e);
            throw e;
        } finally {
            if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                rLock.unlock();
                log.info("自动释放锁成功");
            }
        }
    }
}
